package net.butfly.albacore.dao.base;

import java.io.Serializable;
import java.lang.reflect.Array;

import net.butfly.albacore.criteria.Criteria;
import net.butfly.albacore.entity.BasicEntity;
import net.butfly.albacore.entity.Entity;
import net.butfly.albacore.entity.support.DeleteSupport;
import net.butfly.albacore.entity.support.EntityOPStub;
import net.butfly.albacore.entity.support.UpdateSupport;
import net.butfly.albacore.utils.GenericUtils;

public abstract class EntityDAOBase<E extends BasicEntity<K>, K extends Serializable> extends MybatisDAOBase implements
		EntityDAO<E, K>, MybatisDAO {
	private static final long serialVersionUID = 1L;
	protected Class<E> entityClass = this.getEntityClass();

	protected abstract EntityOPStub<K> getCurrentOPStub();

	@SuppressWarnings("unchecked")
	private Class<E> getEntityClass() {
		return (Class<E>) GenericUtils.getGenericParamClass(this.getClass(), EntityDAOBase.class, "E");
	}

	@Override
	public void insert(E entity) {
		this.template.insert(this.getSqlId(this.entityClass, "insert"), entity);
	}

	@Override
	public void delete(K id) {
		Criteria c = new Criteria();
		c.set(BasicEntity.ID_FIELD, id);
		if (!Entity.class.isAssignableFrom(this.entityClass)) this.template.delete(
				this.getSqlId(this.entityClass, "delete"), c);
		else {
			c.set("deleted", true);
			this.template.update(this.getSqlId(this.entityClass, "delete"), c);
			if (DeleteSupport.class.isAssignableFrom(this.entityClass)) {
				EntityOPStub<K> stub = this.getCurrentOPStub();
				if (null != stub) {
					stub.setId(id);
					this.template.update(this.getSqlId(this.entityClass, "deleteStub"), this.getCurrentOPStub());
				}
			}
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public K[] selectID(Criteria criteria, Class<K> clazz) {
		return this.template.selectList(this.getSqlId(this.entityClass, "selectID"), criteria.getParameters()).toArray(
				(K[]) Array.newInstance(clazz, 0));
	}

	@Override
	public void delete(Criteria criteria) {
		if (!Entity.class.isAssignableFrom(this.entityClass)) this.template.delete(
				this.getSqlId(this.entityClass, "delete"), criteria.getParameters());
		else {
			E[] entities = this.select(criteria);
			if (0 == entities.length) return;
			this.template.update(this.getSqlId(this.entityClass, "delete"), criteria.getParameters());
			if (DeleteSupport.class.isAssignableFrom(this.entityClass)) {
				EntityOPStub<K> stub = this.getCurrentOPStub();
				if (null != stub) for (E e : entities) {
					stub.setId(e.getId());
					this.template.update(this.getSqlId(this.entityClass, "deleteStub"), stub);
				}
			}
		}
	}

	@Override
	public void update(E entity) {
		this.template.update(this.getSqlId(this.entityClass, "update"), entity);
		if (entity instanceof UpdateSupport) {
			EntityOPStub<K> stub = this.getCurrentOPStub();
			if (null != stub) this.template.update(this.getSqlId(this.entityClass, "updateStub"), stub);
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public E load(K id) {
		return (E) this.template.selectOne(this.getSqlId(this.entityClass, "load"), id);
	}

	@SuppressWarnings("unchecked")
	@Override
	public E[] select(Criteria criteria) {
		return null == criteria ? this.template.selectList(this.getSqlId(this.entityClass, "select")).toArray(
				(E[]) Array.newInstance(this.entityClass, 0)) : this.template.selectList(
				this.getSqlId(this.entityClass, "select"), criteria.getParameters()).toArray(
				(E[]) Array.newInstance(this.entityClass, 0));
	}

	@Override
	public int count(Criteria criteria) {
		return null == criteria ? (Integer) this.template.selectOne(this.getSqlId(this.entityClass, "count"))
				: (Integer) this.template.selectOne(this.getSqlId(this.entityClass, "count"), criteria.getParameters());
	}
}
